/*
 Copyright (C) 2007 - Michael Opitz
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "gmail-status-icon.h"
#include "gmail-list.h"

#include <epiphany/ephy-window.h>
#include <epiphany/ephy-statusbar.h>
#include <epiphany/ephy-notebook.h>
#include <epiphany/ephy-shell.h>

#include <libnotify/notify.h>

#include <gtk/gtk.h>
#include <glade/glade.h>

#define LOG(str)			  (printf("%s\n", (str)))

#define GMAIL_ICON_PATH 	\
	"/.gnome2/epiphany/extensions/data/gmailmanager/gmail.png"

#define STATUS_ICON_PRIVATE(o) \
	  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GMAIL_TYPE_STATUS_ICON,\
					GmailStatusIconPrivate))


enum {
	SIG_OPEN_UI = 1,
	SIG_LAST
};

enum {
	PROP_LIST = 1, 
	PROP_WINDOW
};

struct _GmailStatusIconPrivate {
	/** 
	 * The GmailList object.
	 */
	GmailList *list;

	/** 
	 * A pointer to the GtkWindow of the status icon
	 */
	GtkWindow *window;
	/** 
	 * A pointer to the event box in which the status icon is embedded
	 */
	GtkEventBox *evbox;
	/** 
	 * A pointer to the label which indicates the number of the unread
	 * mails.
	 */
	GtkLabel *label;

	/** 
	 * A pointer to the context-menu.
	 */
	GtkMenu *menu;
	/** 
	 * A HashTable which stores menu-items and the associated
	 * gmail-accounts.
	 */
	GHashTable *menu_table;
	/** 
	 * A GSList which stores the menu-group for all accounts
	 */
	GSList* menu_group;

	/** 
	 * A string which indicates the selected GmailAccount.
	 */
	char *menu_selected;

	/** 
	 * A flag which indicates whether the object is disposed or not
	 */
	gboolean is_disposed;
	/** 
	 * A signal handler.
	 */
	guint signal;

	/** 
	 * A handler to a login-timer.
	 */
	guint timer_login;

	/** 
	 * A list which stores all received urls. This list indicates whether a
	 * new url was received or not.
	 */
	GList *urls;

	/** 
	 * A pointer to the popup-window
	 */
	GtkWindow *popup_window;
	/** 
	 * A pointer to the vbox in the popup window
	 */
	GtkVBox *popup_vbox;
	/** 
	 * A hashtable which stores the urls and a associated event-box as key
	 */
	GHashTable *popup_table;
	/** 
	 * A flag which indicates if the popup is currently selected or not
	 */
	gint popup_selected;
};

static GType type;
static guint signals[SIG_LAST];
static GObjectClass *parent_class;

/* normal GObject function declarations. */
static void
gmail_status_icon_class_init (GmailStatusIconClass *klass);
static void
gmail_status_icon_init (GmailStatusIcon *self);
/** 
 * gmail_status_icon_changed_cb:
 * 
 * @list: a pointer to the GmailList object.
 * @self: a poitner to the GmailStatusIcon object.
 *
 * This function is called when the GmailList has changed. It rebuilds the
 * context menu and updates the status icon.
 *
 */
void
gmail_status_icon_changed_cb (GmailList *list, GmailStatusIcon *self);
/** 
 * gmail_status_icon_clear_popup_table:
 * 
 * @key: is the key of the hash table.
 * @val: is the value of the hash table (= url-strings)
 * @user_data: is a gpointer.
 * 
 * This function frees the contents (the url-strings) of the menu hash table.
 *
 */
gboolean
gmail_status_icon_clear_popup_table (gpointer key, gpointer val, 
				     gpointer user_data);
/** 
 * gmail_status_icon_init_gui:
 * 
 * @self: a pointer to a GmailStatusIcon object.
 *
 * This function initializes the GTK-GUI for the status icon.
 *
 */
void
gmail_status_icon_init_gui (GmailStatusIcon *self);
/** 
 * gmail_status_icon_rebuild_menu:
 * 
 * @self: a pointer to a GmailStatusIcon object.
 *
 * This function rebuilds the context menu when the contents of the GmailList
 * have changed.
 *
 */
void
gmail_status_icon_rebuild_menu (GmailStatusIcon *self);
/** 
 * gmail_status_icon_update_icon:
 * 
 * @self: is a pointer to a GmailStatusIcon object
 *
 * This function updates the status icon when the GmailList has changed.
 *
 */
void
gmail_status_icon_update_icon (GmailStatusIcon *self);
/** 
 * gmail_status_icon_clicked_cb:
 * 
 * @item: is a pointer to a GtkMenuItem
 * @event: is a pointer to a GdkEventButton
 * @self: is a pointer to a GmailStatusIcon object
 *
 * This callback is called when the status icon was pressed with the mouse. 
 * 
 * Returns:   
 */
gboolean
gmail_status_icon_clicked_cb (GtkMenuItem *item, GdkEventButton *event, 
			      GmailStatusIcon *self);
/** 
 * gmail_status_icon_enter_cb:
 * 
 * @w: a pointer to a GtkWidget
 * @event: a pointer to a GdkEventCrossing
 * @self: a pointer to a GmailStatusIcon object
 *
 * This callback is connected to a enter-notify-event. It displays the
 * hover-menu when the mouse-cursor enters the status icon.
 *
 */
void
gmail_status_icon_enter_cb (GtkWidget *w, GdkEventCrossing *event,
			    GmailStatusIcon *self);
/** 
 * gmail_status_icon_leave_cb:
 * 
 * @w: a pointer to a GtkWidget
 * @event: a pointer to a GdkEventCrossing
 * @self: a pointer to a GmailStatusIcon object
 *
 * This callback is connected to a leave-notify-event. It destroys the
 * hover-menu when the mouse-cursor leaves the status icon.
 *
 */
void
gmail_status_icon_leave_cb (GtkWindow *w, GdkEventCrossing *event,
			    GmailStatusIcon *self);
/** 
 * gmail_status_icon_handle_login:
 * 
 * @self: a pointer to a GmailStatusIcon.
 *
 * This callback is periodically called and logs all gmail accounts into gmail
 * and checks for new mails.
 * 
 * Returns: always TRUE
 */
gboolean
gmail_status_icon_handle_login (GmailStatusIcon *self);
/** 
 * gmail_status_icon_check_login:
 * 
 * @self: a pointer to a GmailStatusIcon.
 *
 * This function checks whether the login thread has already finished and
 * notifies all listeners when the thread has finished.
 * 
 * Returns: always TRUE
 */
gboolean
gmail_status_icon_check_login (GmailStatusIcon *self);

/** 
 * gmail_status_icon_check_popup_create_urls:
 * 
 * @account: is a pointer to a GmailAccount
 * @self: is a pointer to a GmailStatusIcon 
 *
 * This function is GFunc-function for a GList and gathers all urls from all
 * MessageInfo-structs from all GmailAccounts and stores them in the private url
 * field of GmailStatusIcon.
 *
 */
void
gmail_status_icon_check_popup_create_urls (GmailAccount *account, 
					   GmailStatusIcon *self);
/** 
 * gmail_status_icon_display_libnotify:
 * 
 * @self: a pointer to GmailStatusIcon
 * @num: the number of newly received mails.
 *
 * This function displays the libnotify message for the status icon if new mails
 * were received.
 *
 */
void 
gmail_status_icon_display_libnotify (GmailStatusIcon *self, int num);
/** 
 * gmail_status_icon_create_popup:
 * 
 * @self:
 *
 * This function creates the popup for the selected account.
 *
 */
void
gmail_status_icon_create_popup (GmailStatusIcon *self);
/** 
 * gmail_status_icon_popup_create_entries:
 * 
 * @info: a pointer to a MessageInfo struct.
 * @self: a pointer to a GmailStatusIcon.
 *
 * This function is called by the gmail_status_icon_popup function and creates
 * the GTK-stuff for each MessageInfo struct.
 *
 */
void
gmail_status_icon_popup_create_entries (MessageInfo *info, 
				        GmailStatusIcon *self);
/** 
 * gmail_status_icon_open_browser:
 * 
 * @w: a pointer to a gtk-widget
 * @event: a pointer to a GdkEventButton
 * @self: a pointer to a GmailStatusIcon
 *
 * This function is called when a mail was clicked in the popup window. It opens
 * the corresponding url in a new tab.
 *
 */
void
gmail_status_icon_open_browser (GtkWidget *w, GdkEventButton *event,
			        GmailStatusIcon *self);
/** 
 * gmail_status_icon_popup_helper_cb:
 * 
 * @self: a pointer to a GmailStatusIcon.
 * 
 * This function is a helper function to detect whether to display the popup
 * window or not.
 *
 */
gboolean
gmail_status_icon_popup_helper_cb (GmailStatusIcon *self);
/** 
 * gmail_status_icon_popup_enter_cb:
 * 
 * @w: a pointer to a GtkWidget
 * @ev: a pointer to a GdkEventCrossing
 * @self: a pointer to a GmailStatusIcon
 * 
 * This function is a helper function to detect whether to display the popup
 * window or not.
 *
 */
gboolean
gmail_status_icon_popup_enter_cb (GtkWidget *w, GdkEventCrossing *ev,
				  GmailStatusIcon *self);
/** 
 * gmail_status_icon_popup_leave_cb:
 * 
 * @w: a pointer to a GtkWidget.
 * @ev: a pointer to a GdkEventCrossing.
 * @self: a pointer to a GmailStatusIcon.
 * 
 * This function is a helper function to detect whether to display the popup
 * window or not.
 *
 */
gboolean
gmail_status_icon_popup_leave_cb (GtkWidget *w, GdkEventCrossing *ev, 
				  GmailStatusIcon *self);
/** 
 * gmail_status_icon_build_radio_menu:
 * 
 * @acc: a pointer to a GmailAccount.
 * @self: a pointer to a GmailStatusIcon. 
 *
 * This function builds the radio menu for the context menu for a GmailAccount.
 *
 */
void 
gmail_status_icon_build_radio_menu (GmailAccount *acc, GmailStatusIcon *self);
/** 
 * gmail_status_icon_selected_toggled_cb:
 * 
 * @item: a pointer to a GtkRadioMenuItem.
 * @self: a pointer to a GmailStatusIcon.
 *
 * This callback is called when a radio item in the context menu was selected.
 * It sets the currently active account to the selected account.
 *
 */
void 
gmail_status_icon_selected_toggled_cb (GtkRadioMenuItem *item, 
				       GmailStatusIcon *self);
/** 
 * gmail_status_icon_open_edit_cb:
 * 
 * @w: a pointer to a GtkWidget
 * @self: a pointer to a GmailAccount
 *
 * This callback is called when the edit-button in the context menu was pressed.
 *
 */
void
gmail_status_icon_open_edit_cb (GtkWidget *w, GmailAccount *self);
/** 
 * gmail_status_icon_check_popup:
 * 
 * @self: a pointer to a GmailStatusIcon.
 * @acc: a pointer to a GmailAccount
 *
 * This function checks if a new message arrived for the selected account.
 *
 */
void
gmail_status_icon_check_popup (GmailStatusIcon *self);

GType 
gmail_status_icon_get_type ()
{
	return type;
}

GType
gmail_status_icon_register_type (GTypeModule *module)
{
	const GTypeInfo info = 
	{
		sizeof (GmailStatusIconClass),
		NULL, /* base_init */
		NULL, /* base_finalize */
		(GClassInitFunc) gmail_status_icon_class_init,
		NULL, /* class finalize */
		NULL, /* class_data*/
		sizeof (GmailStatusIcon),
		0, /* n_preallocs */
		(GInstanceInitFunc) gmail_status_icon_init
	};

	type = g_type_module_register_type (module,
					    G_TYPE_OBJECT,
					    "GmailStatusIcon",
					    &info, 0);
	return type;
}

static void
gmail_status_icon_get_property (GObject *object, guint property_id,
			        GValue *value, GParamSpec *pspec)
{
	GmailStatusIcon *self = GMAIL_STATUS_ICON (object);
	switch (property_id) {
	case PROP_LIST:
		g_value_set_pointer (value, self->priv->list);
		break;
	case PROP_WINDOW:
		g_value_set_pointer (value, self->priv->window);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
gmail_status_icon_set_property (GObject *object, guint property_id,
			        const GValue *value, GParamSpec *pspec)
{
	GmailStatusIcon *self = GMAIL_STATUS_ICON (object);
	EphyStatusbar *bar;
	switch (property_id) {
	case PROP_LIST:
		if (self->priv->list != NULL) {
			g_signal_handler_disconnect (self->priv->list, 
						     self->priv->signal);
			g_object_unref (self->priv->list);
		}
		self->priv->list = GMAIL_LIST (g_value_get_pointer(value));
		g_object_ref (self->priv->list);
		self->priv->signal = 
			g_signal_connect (self->priv->list,
					  "gmail-list-changed",
					  G_CALLBACK (
						 gmail_status_icon_changed_cb),
					  self);
		/* log into accounts and display icons... */
		gmail_status_icon_handle_login (self);
		break;
	case PROP_WINDOW:
		if (self->priv->window != NULL) {
			bar = EPHY_STATUSBAR (ephy_window_get_statusbar (
					(EphyWindow*) self->priv->window));
			ephy_statusbar_remove_widget (bar,
					GTK_WIDGET(self->priv->evbox));
		}
		self->priv->window = GTK_WINDOW (g_value_get_pointer (value));
		/* attaching to window... */
		bar = EPHY_STATUSBAR (ephy_window_get_statusbar (
					(EphyWindow*) self->priv->window));
		ephy_statusbar_add_widget (bar, GTK_WIDGET (self->priv->evbox));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
gmail_status_icon_dispose (GObject *object)
{
	LOG ("GmailStatusIcon dispose");
	GmailStatusIcon *self = GMAIL_STATUS_ICON (object);
	EphyStatusbar *bar;

	g_source_remove (self->priv->timer_login);

	self->priv->is_disposed = TRUE;
	if (self->priv->list) {
		g_signal_handler_disconnect (self->priv->list, 
					     self->priv->signal);
		g_object_unref (self->priv->list);
	}
	if (self->priv->window) {
		bar = EPHY_STATUSBAR (ephy_window_get_statusbar(
				(EphyWindow*) self->priv->window));
		ephy_statusbar_remove_widget (bar, 
			GTK_WIDGET (self->priv->evbox));
	}

	if (self->priv->popup_table) {
		g_hash_table_foreach_remove (self->priv->popup_table, 
					    (GHRFunc)
					    gmail_status_icon_clear_popup_table,
					    NULL);
		g_hash_table_unref (self->priv->popup_table);
	}

	if (G_OBJECT_CLASS (parent_class)->dispose)
		G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
gmail_status_icon_finalize (GObject *object)
{
	GmailStatusIcon *self = GMAIL_STATUS_ICON (object);
	g_list_foreach (self->priv->urls, (GFunc) g_free, NULL);
	g_list_free (self->priv->urls);

	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gmail_status_icon_class_init (GmailStatusIconClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GParamSpec *pspec;

	parent_class = g_type_class_peek_parent (klass);
	g_type_class_add_private (klass, sizeof (GmailStatusIconPrivate));

	object_class->get_property = gmail_status_icon_get_property;
	object_class->set_property = gmail_status_icon_set_property;
	object_class->dispose = gmail_status_icon_dispose;
	object_class->finalize = gmail_status_icon_finalize;

	pspec = g_param_spec_pointer ("gmail_list", "Gmail List", 
				      "GmailList which hold all GmailAccounts", 
				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
	g_object_class_install_property (object_class, 
				 	 PROP_LIST,
					 pspec);

	pspec = g_param_spec_pointer ("window", "Window", 
				      "A EphyWindow",
				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
	g_object_class_install_property (object_class, 
					 PROP_WINDOW,
					 pspec);

	signals[SIG_OPEN_UI] = g_signal_new ("open-ui", 
					     G_TYPE_FROM_CLASS (object_class),
					     G_SIGNAL_RUN_FIRST,
					     G_STRUCT_OFFSET (
						    GmailStatusIconClass,
						    open_ui),
					     NULL, NULL,
					     g_cclosure_marshal_VOID__VOID,
					     G_TYPE_NONE, 0);
}

static void
gmail_status_icon_init (GmailStatusIcon *self)
{
	self->priv = STATUS_ICON_PRIVATE (self);
	self->priv->list = NULL;
	self->priv->window = NULL;
	self->priv->evbox = NULL;
	self->priv->menu = NULL;
	self->priv->menu_table = NULL;
	self->priv->menu_selected = NULL;
	self->priv->urls = NULL;
	self->priv->popup_window = NULL;
	self->priv->popup_vbox = NULL;
	self->priv->popup_table = NULL;
	self->priv->popup_selected = 0;
	self->priv->is_disposed = FALSE;

	gmail_status_icon_init_gui (self);
}

static GmailStatusIcon*
gmail_status_icon_new (void)
{
	return g_object_new (GMAIL_TYPE_STATUS_ICON, NULL);
}

void
gmail_status_icon_changed_cb (GmailList *list, GmailStatusIcon *self)
{
	gmail_status_icon_rebuild_menu (self);
	gmail_status_icon_update_icon (self);
}

void
gmail_status_icon_init_gui (GmailStatusIcon *self)
{
	self->priv->evbox = GTK_EVENT_BOX (gtk_event_box_new ());
	gtk_event_box_set_visible_window (GTK_EVENT_BOX (self->priv->evbox), 
					  TRUE);

	/* loading the icon... */
	char *icon_path = g_strconcat (g_get_home_dir (), GMAIL_ICON_PATH, 
				       NULL);
	GtkWidget *icon = gtk_image_new_from_file (icon_path);
	g_free (icon_path);

	self->priv->label = GTK_LABEL (gtk_label_new (""));
	GtkHBox *box = GTK_HBOX (gtk_hbox_new (FALSE, 5));
	gtk_box_pack_start (GTK_BOX (box), icon, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (self->priv->label), TRUE,
			    TRUE, 0);

	gtk_container_add (GTK_CONTAINER (self->priv->evbox), GTK_WIDGET (box));
	gtk_widget_show_all (GTK_WIDGET (self->priv->evbox));

	/* connect signals for hover-effect */
	g_signal_connect_after (self->priv->evbox, "button-press-event",
			        G_CALLBACK (gmail_status_icon_clicked_cb),
			        self);
	g_signal_connect_after (self->priv->evbox, "enter-notify-event",
			        G_CALLBACK (gmail_status_icon_enter_cb),
			        self);
	g_signal_connect_after (self->priv->evbox, "leave-notify-event",
			        G_CALLBACK (gmail_status_icon_leave_cb),
			        self);
	/* set timeouts */
	self->priv->timer_login = 
		g_timeout_add (1000 * 120,
			       (GSourceFunc) gmail_status_icon_handle_login,
			       self);
}

gboolean
gmail_status_icon_handle_login (GmailStatusIcon *self)
{
	if (self->priv->is_disposed || self->priv->list == NULL) return TRUE;
	gmail_list_login (self->priv->list);
	return TRUE;
}

gboolean
gmail_status_icon_check_login (GmailStatusIcon *self)
{
	if (self->priv->is_disposed || self->priv->list == NULL) return TRUE;
	gmail_list_check (self->priv->list);
	return TRUE;
}

gboolean
gmail_status_icon_clicked_cb (GtkMenuItem *item, GdkEventButton *event, 
			      GmailStatusIcon *self)
{
	if (event->button == 1) { 	/* left mouse-button */
		char *user = self->priv->menu_selected;
		if (self->priv->list == NULL || user == NULL) return TRUE;
		GmailAccount *acc = gmail_list_find_account (self->priv->list,
							     user);
		if (acc == NULL) return TRUE;
		char *pass;
		g_object_get (acc, "password", &pass, NULL);
		char *tmp = g_strconcat ("https://www.google.com/accounts/ServiceLoginAuth?ltmpl=yj_wsad&ltmplcache=2&continue=https%3A%2F%2Fmail.google.com%2Fmail%3F&service=mail&rm=false&ltmpl=yj_wsad&Email=", user,"&Passwd=" ,pass, "&rmShown=1&null=Sign+in", NULL);
		EphyTab *tab = ephy_window_get_active_tab (EPHY_WINDOW (
							self->priv->window));
		ephy_shell_new_tab (ephy_shell_get_default (),
				    EPHY_WINDOW (self->priv->window),
			       	    tab, tmp,
				    EPHY_NEW_TAB_OPEN_PAGE | 
				    EPHY_NEW_TAB_IN_EXISTING_WINDOW);
		g_free (tmp);
	} else if (event->button == 2) { 	/* middle mouse */
		gmail_status_icon_handle_login (self);
	} else if (event->button == 3) {		/* right mouse */
		int button, time;
		button = event->button;
		time = event->time;
		if (self->priv->menu == NULL) 
			gmail_status_icon_rebuild_menu (self);
		gtk_menu_popup (GTK_MENU (self->priv->menu), NULL, NULL, NULL,
				NULL, button, time);
	}
	return TRUE;
}

gboolean
gmail_status_icon_clear_popup_table (gpointer key, gpointer val, 
				     gpointer user_data)
{
	g_free ((char*) val);
	return TRUE;
}

void
gmail_status_icon_create_popup (GmailStatusIcon *self)
{
	/* return if there is no account selected, or no account available or no
	 * message infos available
	 */
	if (self->priv->menu_selected == NULL) return;
	GmailAccount *acc = gmail_list_find_account (self->priv->list,
						     self->priv->menu_selected);
	if (acc == NULL) return;
	GList *msgs = NULL;
	g_object_get (acc, "message_infos", &msgs, NULL);
	if (msgs == NULL) return;
	self->priv->popup_window = GTK_WINDOW (gtk_window_new (
					       GTK_WINDOW_POPUP));
	/* we want a blue gmail-border here */
	GdkColor col =  {0, 14756, 20215, 33483};
	gtk_container_set_border_width (GTK_CONTAINER (
					self->priv->popup_window), 1);
	gtk_widget_modify_bg (GTK_WIDGET (self->priv->popup_window), 
			      GTK_STATE_NORMAL, &col);

	/* creating the global vbox for the popup window, and some other widgets 	 */
	GtkVBox *box = GTK_VBOX (gtk_vbox_new (FALSE, 0));
	GtkEventBox *evbox = GTK_EVENT_BOX (gtk_event_box_new ());
	col = (GdkColor) { 0, 65000, 65000, 65000 };
	gtk_widget_modify_bg (GTK_WIDGET (evbox), GTK_STATE_NORMAL, &col);
	gtk_container_add (GTK_CONTAINER (evbox), GTK_WIDGET (box));
	self->priv->popup_vbox = box;
	/* before we actually build a new popup window we should delete the old
	 * hash values... 
	 */
	if (self->priv->popup_table) {
		g_hash_table_foreach_remove (self->priv->popup_table, 
					    gmail_status_icon_clear_popup_table,
					    NULL);
		g_hash_table_unref (self->priv->popup_table);
	}
	self->priv->popup_table = g_hash_table_new (NULL, NULL);
	/* creating the widgets for each message info struct. */
	g_list_foreach (msgs, (GFunc) gmail_status_icon_popup_create_entries,
		        self);

	/* since the width and height of a window is only available when the
	 * window is visible we are using here a dirty hack
	 */
	gtk_container_add (GTK_CONTAINER (self->priv->popup_window),
			   GTK_WIDGET(evbox));
	gtk_window_move (self->priv->popup_window, 20000, 20000); /* HACK! */
	gtk_widget_show_all (GTK_WIDGET (self->priv->popup_window));

	/* aligning the window */
	int x, y;
	int my_w, my_h;

	GtkWidget *widget = GTK_WIDGET (self->priv->evbox);
	gdk_window_get_origin (widget->window, &x, &y);
	gtk_window_get_size (GTK_WINDOW (self->priv->popup_window), 
			     &my_w, &my_h);

	gtk_window_move (GTK_WINDOW (self->priv->popup_window), x, y - (my_h));
	gtk_widget_show_all (GTK_WIDGET (self->priv->popup_window));
}

void
gmail_status_icon_popup_create_entries (MessageInfo *info, 
					GmailStatusIcon *self)
{
	GtkVBox *vbox = self->priv->popup_vbox;
	GtkHBox *hbox = GTK_HBOX (gtk_hbox_new (FALSE, 0));
	GtkVBox *box = GTK_VBOX (gtk_vbox_new (FALSE, 0));
	GtkWidget *align;

	char *tmp = NULL;
	tmp = g_markup_printf_escaped (
		"<span weight='bold' underline='single'>%s</span>",
		info->subject);
	GtkLabel *label = GTK_LABEL (gtk_label_new (NULL));
	gtk_label_set_markup (label, tmp);

	align = gtk_alignment_new (0, 0, 0, 0);
	GtkEventBox *evbox = GTK_EVENT_BOX (gtk_event_box_new ());
	GdkColor col = (GdkColor) {0, 65000, 65000, 65000 };
	gtk_widget_modify_bg (GTK_WIDGET (evbox), GTK_STATE_NORMAL, &col);

	gtk_container_add (GTK_CONTAINER (evbox), GTK_WIDGET (align));
	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (label));
	gtk_box_pack_start (GTK_BOX(box), GTK_WIDGET (evbox), TRUE, TRUE, 0);
	g_free (tmp);

	g_hash_table_insert (self->priv->popup_table, evbox,
			     g_strdup (info->url));
	g_signal_connect_after (evbox, "button-press-event",
			G_CALLBACK (gmail_status_icon_open_browser),
			self);

	tmp = g_markup_printf_escaped (
		"<span weight='bold'>From: %s</span>\n"
		"%s", 
		info->from_name, info->snipped);
	label = GTK_LABEL (gtk_label_new (NULL));
	gtk_label_set_markup (label, tmp);
	gtk_label_set_line_wrap (label, TRUE);
	g_free (tmp);

	align = gtk_alignment_new (0, 0, 0, 0);
	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (label));

	gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (align), TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (box), TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (hbox), TRUE, TRUE, 0);
}

void
gmail_status_icon_open_browser (GtkWidget *w, GdkEventButton *event,
			        GmailStatusIcon *self)
{
	if (self->priv->is_disposed) return;

	/* we are opening the selected url in a new tab. This may should be
	 * configurable in future versions...
	 */
	char *url = (char*) g_hash_table_lookup (self->priv->popup_table, w);
	EphyWindow *wnd = EPHY_WINDOW (self->priv->window);
	EphyTab *tab = ephy_window_get_active_tab (wnd);
	ephy_shell_new_tab (ephy_shell_get_default (), 
			    wnd, tab, url,
			    EPHY_NEW_TAB_OPEN_PAGE |
			    EPHY_NEW_TAB_IN_EXISTING_WINDOW);
}

gboolean
gmail_status_icon_popup_helper_cb(GmailStatusIcon *self)
{
	if (!self->priv->popup_window) {
		return FALSE;
	}
	if (self->priv->popup_selected == 2 || 
	    self->priv->popup_selected == 0) {
		gtk_widget_hide (GTK_WIDGET (self->priv->popup_window));
		gtk_widget_destroy (GTK_WIDGET (self->priv->popup_window));
		self->priv->popup_window = NULL;

		g_hash_table_foreach_remove (self->priv->popup_table, 
					    gmail_status_icon_clear_popup_table,
					    NULL);
		g_hash_table_unref (self->priv->popup_table);
		self->priv->popup_table = NULL;
	}
	return FALSE;
}

gboolean
gmail_status_icon_popup_enter_cb (GtkWidget *w, GdkEventCrossing *ev,
				  GmailStatusIcon *self)
{
	self->priv->popup_selected = 1;
	return TRUE;
}

gboolean
gmail_status_icon_popup_leave_cb (GtkWidget *w, GdkEventCrossing *ev, 
				  GmailStatusIcon *self)
{
	g_timeout_add (100, 
		      (GSourceFunc) gmail_status_icon_popup_helper_cb,
		      self);
	self->priv->popup_selected = 2;
	return TRUE;
}

void
gmail_status_icon_enter_cb (GtkWidget *w, GdkEventCrossing *event,
			    GmailStatusIcon *self)
{
	if (self->priv->popup_window) return;
	gmail_status_icon_create_popup (self);
	g_signal_connect_after (self->priv->popup_window, "leave-notify-event",
			        G_CALLBACK (gmail_status_icon_popup_leave_cb), 
				self);
	g_signal_connect_after (self->priv->popup_window, "enter-notify-event",
			        G_CALLBACK (gmail_status_icon_popup_enter_cb), 
				self);
}

void
gmail_status_icon_leave_cb (GtkWindow *w, GdkEventCrossing *event,
			    GmailStatusIcon *self)
{
	if (self->priv->popup_window == NULL) return;
	self->priv->popup_selected = 2;
	g_timeout_add (100,
		       (GSourceFunc) gmail_status_icon_popup_helper_cb, 
		       self);
}

static int gmail_status_icon_first_hack = 0;

void 
gmail_status_icon_build_radio_menu (GmailAccount *acc, GmailStatusIcon *self)
{	
	/* filling the context menu with the email addresses... */
	char *mail;
	g_object_get (acc, "mail", &mail, NULL);
	GtkRadioMenuItem *item =
		GTK_RADIO_MENU_ITEM (gtk_radio_menu_item_new_with_label(
					self->priv->menu_group,
					mail));
	self->priv->menu_group = gtk_radio_menu_item_get_group (item);
	g_hash_table_insert (self->priv->menu_table, item, acc);

	g_signal_connect_after (GTK_RADIO_MENU_ITEM (item), "toggled", 
			        (GCallback)
			        gmail_status_icon_selected_toggled_cb,
			        self);

	if (self->priv->menu_selected != NULL && 
	    !strcmp (mail, self->priv->menu_selected)) {
		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), 
						TRUE);
	}
	if (self->priv->menu_selected == NULL && 
	    gmail_status_icon_first_hack == 0) {
		gmail_status_icon_first_hack = 1;
		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), 
						TRUE);
	}
	gtk_menu_shell_append (GTK_MENU_SHELL (self->priv->menu), 
			       GTK_WIDGET (item));
	gtk_widget_show (GTK_WIDGET (item));
}

void
gmail_status_icon_rebuild_menu (GmailStatusIcon *self)
{
	if (self->priv->menu) {
		gtk_widget_destroy (GTK_WIDGET (self->priv->menu));
	}
	self->priv->menu = GTK_MENU (gtk_menu_new ());
	GtkMenuItem *edit = GTK_MENU_ITEM (
				gtk_menu_item_new_with_label ("Edit Accounts"));
	gtk_menu_shell_append (GTK_MENU_SHELL (self->priv->menu),
			       GTK_WIDGET (edit));

	if (self->priv->list == NULL) return;
	/* building the selection */
	if (self->priv->menu_table) {
		g_hash_table_unref (self->priv->menu_table);
	}
	self->priv->menu_table = g_hash_table_new (NULL, NULL);
	gmail_status_icon_first_hack = 0;
	self->priv->menu_group = NULL;

	gmail_list_foreach (self->priv->list, 
			    (GFunc) gmail_status_icon_build_radio_menu, self);

	g_signal_connect (edit,
			  "activate",
			  (GCallback) gmail_status_icon_open_edit_cb,
			  self);
	gtk_widget_show_all (GTK_WIDGET (self->priv->menu));
}

void
gmail_status_icon_open_edit_cb (GtkWidget *w, GmailAccount *self)
{
	g_signal_emit (self, signals[SIG_OPEN_UI], 0);	/* fire
							   listeners.. */
}

void
gmail_status_icon_update_icon (GmailStatusIcon *self)
{
	GList *lst = NULL;
	GList *msgs = NULL;
	GmailAccount *acc = NULL;
	char *mail = NULL;
	if (self->priv->menu_selected != NULL) {
		acc = gmail_list_find_account (self->priv->list, 
					       self->priv->menu_selected);
	}
	if (self->priv->menu_selected == NULL || acc == NULL) {
		/* select first account */
		g_object_get (self->priv->list, "accounts", &lst, NULL);
		if (lst != NULL) {
			acc = (GmailAccount*) lst->data;
			g_object_get (acc, "mail", &mail, NULL);
			self->priv->menu_selected = mail;
			g_list_free (lst);
		}
	}
	if (self->priv->menu_selected == NULL) return;
	if (acc == NULL) return;
	/* count the messages */
	g_object_get (acc, "message_infos", &msgs, NULL);
	char *txt = g_strdup_printf ("%d", g_list_length (msgs));
	gtk_label_set_text (self->priv->label, txt);
	gmail_status_icon_check_popup (self);
	g_free (txt);
}

void
gmail_status_icon_check_popup_create_urls (GmailAccount *account, 
					   GmailStatusIcon *self)
{
	GList *msgs;
	g_object_get (account, "message_infos", &msgs, NULL);
	/* 
	 * append the list of urls from the message_info-struct to the
	 * GmailStatusIcon's url-GList object
	 */
	for (; msgs != NULL; msgs = msgs->next) {
		MessageInfo *info = (MessageInfo*) msgs->data;
		char *tmp = g_strdup (info->url);
		self->priv->urls = g_list_append (self->priv->urls, tmp);
	}
}

void 
gmail_status_icon_display_libnotify (GmailStatusIcon *self, int num)
{
	NotifyNotification *n;
	notify_init ("Epiphany");
	char *str = NULL;

	/* if we have > 1 new mails we must display the plural message */
	if (num > 1) {
		str = g_strdup_printf ("You have %d new mails.", num);
	} else {
		str = g_strdup_printf ("You have %d new mail.", num);
	}

	n = notify_notification_new ("New Mail",
				     str, NULL, NULL);
	/* we'll show the message for about 5 seconds */
	notify_notification_set_timeout (n, 5000); 
	notify_notification_attach_to_widget (n, GTK_WIDGET (
				self->priv->evbox));

	if (!notify_notification_show (n, NULL)) {
		fprintf (stderr, "failed to send notification\n");
	}
	g_object_unref (G_OBJECT (n));
	g_free (str);
}

void
gmail_status_icon_check_popup (GmailStatusIcon *self)
{
	/* 
	 * check whether we have actually new urls in any of our MessageInfo
	 * structs.
	 */
	GList *tmp = self->priv->urls;
	self->priv->urls = NULL;
	/*
	 * First of all make a list of all available urls
	 */
	gmail_list_foreach (self->priv->list, 
			    (GFunc) gmail_status_icon_check_popup_create_urls,
			    self);

	GList *i = self->priv->urls;
	int display = 0;
	/* now compare the new set with the old list */
	for (; i != NULL; i = i->next) {
		GList *rtrn = 
			g_list_find_custom (tmp, i->data, 
					    (GCompareFunc)strcmp);
		if (rtrn == NULL) {	/* we've actually found a new message */
			++display;
		}
	}

	/* 
	 * If we have new mail we are going to display a new libnotify 
	 * message 
	 */
	if (display > 0) { 	/* TODO: insert libnotify-code here */
		gmail_status_icon_display_libnotify (self, display);
	}

	/* cleanup the tmp-list */
	if (tmp != NULL) {
		g_list_foreach (tmp, (GFunc) g_free, NULL);
		g_list_free (tmp);
	}
}

void 
gmail_status_icon_selected_toggled_cb (GtkRadioMenuItem *item, 
				       GmailStatusIcon *self)
{
	if (gtk_check_menu_item_get_active (
	    GTK_CHECK_MENU_ITEM (item)) == FALSE) {
		return;
	}
	GmailAccount *acc = g_hash_table_lookup (self->priv->menu_table, item);
	if (acc == NULL) {
		LOG ("Fatal Error");
		return;
	}
	if (self->priv->menu_selected) {
		g_free (self->priv->menu_selected);
		self->priv->menu_selected = NULL;
	}
	g_object_get (acc, "mail", &self->priv->menu_selected, NULL);
	gmail_status_icon_update_icon (self);
}

